package com.tca.common.learning.datasource;

import com.google.common.collect.Lists;
import com.tca.common.core.utils.ValidateUtils;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;

/**
 * @author zhoua
 * @date 2024/1/6 11:01
 */
@NoArgsConstructor
@Data
@Slf4j
public class CustomDataSource implements ICustomDataSource{

    private DataSourceProperties dataSourceProperties;

    /**
     * 空闲连接列表 - 连接代理
     */
    private final List<Connection> idleConnections = Lists.newArrayList();

    /**
     * 活跃连接列表 - 连接对象
     */
    private final List<Connection> activeConnections = Lists.newArrayList();

    /**
     * 锁对象
     */
    private final Object monitor = new Object();

    public CustomDataSource(DataSourceProperties dataSourceProperties) {
        this.dataSourceProperties = dataSourceProperties;
    }

    @Override
    public Connection getConnection() throws SQLException {
        return this.getConnection(dataSourceProperties.getUsername(),
                dataSourceProperties.getPassword());
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return this.doGetConnection(username, password);
    }

    /**
     * 获取连接
     * @param username
     * @param password
     * @return
     * @throws SQLException
     */
    private Connection doGetConnection(String username, String password) throws SQLException {
        while (true) {
            synchronized (monitor) {
                // 有空闲连接
                if (ValidateUtils.isNotEmpty(idleConnections)) {
                    Connection connection = idleConnections.remove(0);
                    activeConnections.add(connection);
                    log.info("从idleConnections中获取连接, active = {}, idle = {}",
                            activeConnections.size(), idleConnections.size());
                    return connection;
                }

                // 没有空闲连接
                // 已经达到最大连接数
                if (activeConnections.size() == dataSourceProperties.getMaxConnectionNum()) {
                    try {
                        log.info("活跃连接已经达到最大, 需等待, active = {}, idle = {}",
                                activeConnections.size(), idleConnections.size());
                        monitor.wait(dataSourceProperties.getPoolTimeToWait());
                    } catch (InterruptedException e) {
                        log.error("获取连接超时", e);
                        return null;
                    }
                } else {
                    // 没有达到最大连接数, 这里需要创建的是代理连接
                    Connection connection = DriverManager.getConnection(dataSourceProperties.getUrl(),
                            username, password);
                    Connection connectionProxy = (Connection)Proxy.newProxyInstance(connection.getClass().getClassLoader(),
                            new Class[]{Connection.class},
                            new DataSourceProxyHandler(connection,this));
                    activeConnections.add(connectionProxy);
                    log.info("活跃连接未达到最大, 创建连接, active = {}, idle = {}",
                            activeConnections.size(), idleConnections.size());
                    return connectionProxy;
                }
            }
        }

    }

    /**
     * 归还连接
     * @param connection
     */
    public void doReturn(Connection connection) {
        synchronized (monitor) {
            boolean result = activeConnections.remove(connection);
            if (idleConnections.size() < dataSourceProperties.getMaxIdleConnectionNum()) {
                idleConnections.add(connection);
                log.info("归还连接进入到idle中, active = {}, idle = {}",
                        activeConnections.size(), idleConnections.size());
            } else {
                // 如果空闲列表已满, 需要手动关闭连接
                try {
                    // 获取目标对象, 调用目标对象的close()
                    this.doClose(connection);
                    log.info("idle连接已满, 需要关闭, active = {}, idle = {}",
                            activeConnections.size(), idleConnections.size());
                } catch (SQLException e) {
                    log.error("连接关闭失败", e);
                }
            }

            monitor.notify();
        }
    }

    /**
     * 关闭连接
     * @param connection
     * @throws SQLException
     */
    public void doClose(Connection connection) throws SQLException {
        InvocationHandler invocationHandler = Proxy.getInvocationHandler(connection);
        ((DataSourceProxyHandler)invocationHandler).getObjectConnection().close();
    }
}
